/*
*
*/

#include <stdlib.h>
#include <stdio.h>

#define ds_nifi_c
#define LUA_LIB

#include <MessageQueue.h>
#include <802.11.h>
#include <lobby.h>

#include "../../lua-5.1.3/src/lparser.h"

#include "vars.h"

static int nifi_init(lua_State *L) {
    bool lobby = LOBBY_Init();
    assert(L, lobby, "Can't initialize NiFi correctly.");
    return 0;
}

static int nifi_update(lua_State *L) {
    //IPC_RcvCompleteCheck();
    LOBBY_Update();
    return 0;
}

static int nifi_setOwnName(lua_State *L) {
    char * name = (char *)luaL_checkstring(L, 1);
    LOBBY_SetOwnName(name);
    return 0;
}

static int nifi_getUserByID(lua_State *L) {
    unsigned short id = (unsigned short)luaL_checknumber(L, 1);
    lua_pushlightuserdata(L, LOBBY_GetUserByID(id));
    return 1;
}

static int nifi_getUserByMAC(lua_State *L) {
    unsigned char * mac = (unsigned char *)luaL_checkstring(L, 1);
    lua_pushlightuserdata(L, LOBBY_GetUserByMAC(mac));
    return 1;
}

static int nifi_getNumberOfKnownUsers(lua_State *L) {
    lua_pushnumber(L, LOBBY_GetNumberOfKnownUsers());
    return 1;
}

static int nifi_isUserTimedOut(lua_State *L) {
    LPLOBBY_USER user = lua_touserdata(L, 1);
    lua_pushnumber(L, LOBBY_IsTimedOut(user));
    return 1;
}

static int nifi_userResetTimeout(lua_State *L) {
    LPLOBBY_USER user = lua_touserdata(L, 1);
    LOBBY_UserResetTimeout(user);
    return 0;
}

static int nifi_getUserName(lua_State *L) {
    LPLOBBY_USER user = lua_touserdata(L, 1);
    lua_pushstring(L, LOBBY_GetUserName(user));
    return 1;
}

static int nifi_userIsAllSent(lua_State *L) {
    LPLOBBY_USER user = lua_touserdata(L, 1);
    lua_pushnumber(L, LOBBY_UserIsAllSent(user));
    return 1;
}

static int nifi_userIsStreamSent(lua_State *L) {
    LPLOBBY_USER user = lua_touserdata(L, 1);
    unsigned short streamID = (unsigned short)luaL_checknumber(L, 2);
    lua_pushnumber(L, LOBBY_UserIsStreamSent(user, streamID));
    return 1;
}

static int nifi_createRoom(lua_State *L) {
    char * name = (char *)luaL_checkstring(L, 1);
    int maxUsers = (int)luaL_checknumber(L, 2);
    unsigned short gameCode = (unsigned short)luaL_checknumber(L, 3);
    unsigned short gameVersion = (unsigned short)luaL_checknumber(L, 4);
    LOBBY_CreateRoom(name, maxUsers, gameCode, gameVersion);
    return 0;
}

static int nifi_joinRoom(lua_State *L) {
    LPLOBBY_ROOM room = lua_touserdata(L, 1);
    LOBBY_JoinRoom(room);
    return 0;
}

static int nifi_leaveRoom(lua_State *L) {
    LOBBY_LeaveRoom();
    return 0;
}

static int nifi_setRoomVisibility(lua_State *L) {
    bool visible = (bool)lua_toboolean(L, 1);
    LOBBY_SetRoomVisibility(visible);
    return 0;
}

static int nifi_getNumberOfKnownRooms(lua_State *L) {
    lua_pushnumber(L, LOBBY_GetNumberOfKnownRooms());
    return 1;
}

static int nifi_getRoomByID(lua_State *L) {
    unsigned long id = (unsigned long)luaL_checknumber(L, 1);
    lua_pushlightuserdata(L, LOBBY_GetRoomByID(id));
    return 1;
}

static int nifi_getRoomByMAC(lua_State *L) {
    unsigned char * mac = (unsigned char *)luaL_checkstring(L, 1);
    lua_pushlightuserdata(L, LOBBY_GetRoomByMAC(mac));
    return 1;
}

static int nifi_getRoomByUser(lua_State *L) {
    LPLOBBY_USER user = lua_touserdata(L, 1);
    lua_pushlightuserdata(L, LOBBY_GetRoomByUser(user));
    return 1;
}

static int nifi_getRoomByGame(lua_State *L) {
    LPLOBBY_ROOM anchor = lua_touserdata(L, 1);
    if (lua_isnumber(L, 1)) {
        anchor = 0;
    }
    unsigned long gameCode = (unsigned long)luaL_checknumber(L, 2);
    lua_pushlightuserdata(L, LOBBY_GetRoomByGame(anchor, gameCode));
    return 1;
}

static int nifi_getUsercountInRoom(lua_State *L) {
    LPLOBBY_ROOM room = lua_touserdata(L, 1);
    lua_pushnumber(L, LOBBY_GetUsercountInRoom(room));
    return 1;
}

static int nifi_getMaxUsercountInRoom(lua_State *L) {
    LPLOBBY_ROOM room = lua_touserdata(L, 1);
    lua_pushnumber(L, LOBBY_GetMaxUsercountInRoom(room));
    return 1;
}

static int nifi_getRoomUserBySlot(lua_State *L) {
    LPLOBBY_ROOM room = lua_touserdata(L, 1);
    unsigned char slot = (unsigned char)luaL_checkstring(L, 2);
    lua_pushlightuserdata(L, LOBBY_GetRoomUserBySlot(room, slot));
    return 1;
}

static int nifi_getRoomName(lua_State *L) {
    LPLOBBY_ROOM room = lua_touserdata(L, 1);
    lua_pushstring(L, LOBBY_GetRoomName(room));
    return 1;
}

static int nifi_getRoomGameCode(lua_State *L) {
    LPLOBBY_ROOM room = lua_touserdata(L, 1);
    lua_pushnumber(L, LOBBY_GetRoomGameCode(room));
    return 1;
}

static int nifi_getRoomGameVersion(lua_State *L) {
    LPLOBBY_ROOM room = lua_touserdata(L, 1);
    lua_pushnumber(L, LOBBY_GetRoomGameVersion(room));
    return 1;
}

// LOBBY_SetStreamHandler(unsigned short streamID, LOBBY_STREAMHANDLER_PROC callback)

static int nifi_setStreamHandler(lua_State *L) {
    unsigned short streamID = (unsigned short)luaL_checknumber(L, 1);
    LOBBY_STREAMHANDLER_PROC callback = (LOBBY_STREAMHANDLER_PROC)lua_tocfunction(L, 2);
    LOBBY_SetStreamHandler(streamID, callback);
    return 0;
}
  
static int nifi_sendToUser(lua_State *L) {
    LPLOBBY_USER user = lua_touserdata(L, 1);
    unsigned short stream = (unsigned short)luaL_checknumber(L, 2);
    /*if (lua_isnumber(L, 3)) {
    	int data = (int)luaL_checknumber(L, 3);
    } else if (lua_isstring(L, 3)) {
    	char * data = (char *)luaL_checkstring(L, 3);
    } else if (lua_isuserdata(L, 3)) {
    	unsigned char * data = lua_touserdata(L, 3);
    }*/
    void * data = lua_touserdata(L, 3);
    //int length = (int)luaL_checknumber(L, 4);
    LOBBY_SendToUser(user, stream, (unsigned char *)data, sizeof((unsigned char *)data));//length);
    return 0;
}

static int nifi_sendToRoom(lua_State *L) {
    LPLOBBY_ROOM room = lua_touserdata(L, 1);
    unsigned short stream = (unsigned short)luaL_checknumber(L, 2);
    /*if (lua_isnumber(L, 3)) {
    	int data = (int)luaL_checknumber(L, 3);
    } else if (lua_isstring(L, 3)) {
    	char * data = (char *)luaL_checkstring(L, 3);
    } else if (lua_isuserdata(L, 3)) {
    	unsigned char * data = lua_touserdata(L, 3);
    }*/
    void * data = lua_touserdata(L, 3);
    //int length = (int)luaL_checknumber(L, 4);
    LOBBY_SendToRoom(room, stream, (unsigned char *)data, sizeof((unsigned char *)data));//length);
    return 0;
}

static int nifi_sendToAll(lua_State *L) {
    unsigned short stream = (unsigned short)luaL_checknumber(L, 1);
    /*if (lua_isnumber(L, 2)) {
    	int data = (int)luaL_checknumber(L, 2);
    } else if (lua_isstring(L, 2)) {
    	char * data = (char *)luaL_checkstring(L, 2);
    } else if (lua_isuserdata(L, 2)) {
    	unsigned char * data = lua_touserdata(L, 2);
    }*/
    void * data = lua_touserdata(L, 2);
    //int length = (int)luaL_checknumber(L, 3);
    LOBBY_SendToAll(stream, (unsigned char *)data, sizeof((unsigned char *)data));//length);
    return 0;
}

static int nifi_broadcast(lua_State *L) {
    unsigned short stream = (unsigned short)luaL_checknumber(L, 1);
    /*if (lua_isnumber(L, 2)) {
    	int data = (int)luaL_checknumber(L, 2);
    } else if (lua_isstring(L, 2)) {
    	char * data = (char *)luaL_checkstring(L, 2);
    } else if (lua_isuserdata(L, 2)) {
    	unsigned char * data = lua_touserdata(L, 2);
    }*/
    void * data = lua_touserdata(L, 3);
    //int length = (int)luaL_checknumber(L, 3);
    LOBBY_Broadcast(stream, (unsigned char *)data, sizeof((unsigned char *)data));//length);
    return 0;
}

// LOBBY_SetUserInfoCallback(LOBBY_USERINFOHANDLER_PROC callback)

static const luaL_Reg nifilib[] = {
    {"init", nifi_init},
    {"update", nifi_update},
    {"setOwnName", nifi_setOwnName},
    {"getUserByID", nifi_getUserByID},
    {"getUserByMAC", nifi_getUserByMAC},
    {"getNumberOfKnownUsers", nifi_getNumberOfKnownUsers},
    {"isUserTimedOut", nifi_isUserTimedOut},
    {"userResetTimeout", nifi_userResetTimeout},
    {"getUserName", nifi_getUserName},
    {"userIsAllSent", nifi_userIsAllSent},
    {"userIsStreamSent", nifi_userIsStreamSent},
    {"createRoom", nifi_createRoom},
    {"joinRoom", nifi_joinRoom},
    {"leaveRoom", nifi_leaveRoom},
    {"setRoomVisibility", nifi_setRoomVisibility},
    {"getNumberOfKnownRooms", nifi_getNumberOfKnownRooms},
    {"getRoomByID", nifi_getRoomByID},
    {"getRoomByMAC", nifi_getRoomByMAC},
    {"getRoomByUser", nifi_getRoomByUser},
    {"getRoomByGame", nifi_getRoomByGame},
    {"getUsercountInRoom", nifi_getUsercountInRoom},
    {"getMaxUsercountInRoom", nifi_getMaxUsercountInRoom},
    {"getRoomUserBySlot", nifi_getRoomUserBySlot},
    {"getRoomName", nifi_getRoomName},
    {"getRoomGameCode", nifi_getRoomGameCode},
    {"getRoomGameVersion", nifi_getRoomGameVersion},
    {"setStreamHandler", nifi_setStreamHandler},
    {"sendToUser", nifi_sendToUser},
    {"sendToRoom", nifi_sendToRoom},
    {"sentToAll", nifi_sendToAll},
    {"broadcast", nifi_broadcast},
    //{"setUserInfoCallback", nifi_setUserInfoCallback}
    {NULL, NULL}
};

/*
** Open infos library
*/
LUALIB_API int luaopen_nifi (lua_State *L) {
    luaL_register(L, LUA_NIFILIBNAME, nifilib);
    return 1;
}
